{
struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
trap_info_t *ti;
- unsigned long addr, fixup;
+ unsigned long off, addr, fixup;
struct task_struct *p = current;
extern int map_ldt_shadow_page(unsigned int);
__asm__ __volatile__ ("movl %%cr2,%0" : "=r" (addr) : );
- if ( unlikely(addr > PAGE_OFFSET) )
- goto fault_in_xen_space;
-
- propagate_fault:
+ if ( unlikely(addr >= LDT_VIRT_START) &&
+ (addr < (LDT_VIRT_START + (p->mm.ldt_ents*LDT_ENTRY_SIZE))) )
+ {
+ /*
+ * Copy a mapping from the guest's LDT, if it is valid. Otherwise we
+ * send the fault up to the guest OS to be handled.
+ */
+ off = addr - LDT_VIRT_START;
+ addr = p->mm.ldt_base + off;
+ if ( likely(map_ldt_shadow_page(off >> PAGE_SHIFT) == 0) )
+ return; /* successfully copied the mapping */
+ }
if ( unlikely(!(regs->xcs & 3)) )
goto fault_in_hypervisor;
clear_bit(EVENTS_MASTER_ENABLE_BIT, &p->shared_info->events_mask);
return;
- /*
- * FAULT IN XEN ADDRESS SPACE:
- * We only deal with one kind -- a fault in the shadow LDT mapping.
- * If this occurs we pull a mapping from the guest's LDT, if it is
- * valid. Otherwise we send the fault up to the guest OS to be handled.
- */
- fault_in_xen_space:
-
- if ( (addr < LDT_VIRT_START) ||
- (addr >= (LDT_VIRT_START + (p->mm.ldt_ents*LDT_ENTRY_SIZE))) ||
- map_ldt_shadow_page((addr - LDT_VIRT_START) >> PAGE_SHIFT) )
- goto propagate_fault;
- return;
-
fault_in_hypervisor:
if ( likely((fixup = search_exception_table(regs->eip)) != 0) )
# Hypervisor uses this for application faults while it executes.
ENTRY(failsafe_callback)
+ call SYMBOL_NAME(install_safe_pf_handler)
1: pop %ds
2: pop %es
3: pop %fs
4: pop %gs
+ call SYMBOL_NAME(install_normal_pf_handler)
5: iret
.section .fixup,"ax"; \
6: movl $0,(%esp); \
# This handler is special, because it gets an extra value on its stack,
# which is the linear faulting address.
-ENTRY(page_fault)
- pushl %ds
- pushl %eax
- xorl %eax,%eax
- pushl %ebp
- pushl %edi
- pushl %esi
- pushl %edx
- decl %eax # eax = -1
- pushl %ecx
- pushl %ebx
- GET_CURRENT(%ebx)
- cld
- movl %es,%ecx
- movl ORIG_EAX(%esp), %esi # get the error code
- movl ES(%esp), %edi # get the faulting address
- movl %eax, ORIG_EAX(%esp)
- movl %ecx, ES(%esp)
- movl %esp,%edx
- pushl %edi # push the faulting address
- pushl %esi # push the error code
- pushl %edx # push the pt_regs pointer
- movl $(__KERNEL_DS),%edx
- movl %edx,%ds
- movl %edx,%es
- call SYMBOL_NAME(do_page_fault)
- addl $12,%esp
- jmp ret_from_exception
+#define PAGE_FAULT_STUB(_name1, _name2) \
+ENTRY(_name1) \
+ pushl %ds ; \
+ pushl %eax ; \
+ xorl %eax,%eax ; \
+ pushl %ebp ; \
+ pushl %edi ; \
+ pushl %esi ; \
+ pushl %edx ; \
+ decl %eax /* eax = -1 */ ; \
+ pushl %ecx ; \
+ pushl %ebx ; \
+ GET_CURRENT(%ebx) ; \
+ cld ; \
+ movl %es,%ecx ; \
+ movl ORIG_EAX(%esp), %esi /* get the error code */ ; \
+ movl ES(%esp), %edi /* get the faulting address */ ; \
+ movl %eax, ORIG_EAX(%esp) ; \
+ movl %ecx, ES(%esp) ; \
+ movl %esp,%edx ; \
+ pushl %edi /* push the faulting address */ ; \
+ pushl %esi /* push the error code */ ; \
+ pushl %edx /* push the pt_regs pointer */ ; \
+ movl $(__KERNEL_DS),%edx ; \
+ movl %edx,%ds ; \
+ movl %edx,%es ; \
+ call SYMBOL_NAME(_name2) ; \
+ addl $12,%esp ; \
+ jmp ret_from_exception ;
+PAGE_FAULT_STUB(page_fault, do_page_fault)
+PAGE_FAULT_STUB(safe_page_fault, do_safe_page_fault)
ENTRY(machine_check)
pushl $0
asmlinkage void stack_segment(void);
asmlinkage void general_protection(void);
asmlinkage void page_fault(void);
+asmlinkage void safe_page_fault(void);
asmlinkage void coprocessor_error(void);
asmlinkage void simd_coprocessor_error(void);
asmlinkage void alignment_check(void);
3, __KERNEL_CS, (unsigned long)system_call },
{ 0, 0, 0, 0 }
};
-
void __init trap_init(void)
cpu_init();
}
+
+
+/*
+ * install_safe_pf_handler / install_normal_pf_handler:
+ *
+ * These are used within the failsafe_callback handler in entry.S to avoid
+ * taking a full page fault when reloading FS and GS. This is because FS and
+ * GS could be invalid at pretty much any point while Xenolinux executes (we
+ * don't set them to safe values on entry to the kernel). At *any* point Xen
+ * may be entered due to a hardware interrupt --- on exit from Xen an invalid
+ * FS/GS will cause our failsafe_callback to be executed. This could occur,
+ * for example, while the mmmu_update_queue is in an inconsistent state. This
+ * is disastrous because the normal page-fault handler touches the update
+ * queue!
+ *
+ * Fortunately, within the failsafe handler it is safe to force DS/ES/FS/GS
+ * to zero if they cannot be reloaded -- at this point executing a normal
+ * page fault would not change this effect. The safe page-fault handler
+ * ensures this end result (blow away the selector value) without the dangers
+ * of the normal page-fault handler.
+ *
+ * NB. Perhaps this can all go away after we have implemented writeable
+ * page tables. :-)
+ */
+
+asmlinkage void do_safe_page_fault(struct pt_regs *regs,
+ unsigned long error_code,
+ unsigned long address)
+{
+ unsigned long fixup;
+
+ if ( (fixup = search_exception_table(regs->eip)) != 0 )
+ {
+ regs->eip = fixup;
+ return;
+ }
+
+ die("Unhandleable 'safe' page fault!", regs, error_code);
+}
+
+unsigned long install_safe_pf_handler(void)
+{
+ static trap_info_t safe_pf[] = {
+ { 14, 0, __KERNEL_CS, (unsigned long)safe_page_fault },
+ { 0, 0, 0, 0 }
+ };
+ unsigned long flags;
+ local_irq_save(flags);
+ HYPERVISOR_set_trap_table(safe_pf);
+ return flags; /* This is returned in %%eax */
+}
+
+__attribute__((regparm(3))) /* This function take its arg in %%eax */
+void install_normal_pf_handler(unsigned long flags)
+{
+ static trap_info_t normal_pf[] = {
+ { 14, 0, __KERNEL_CS, (unsigned long)page_fault },
+ { 0, 0, 0, 0 }
+ };
+ HYPERVISOR_set_trap_table(normal_pf);
+ local_irq_restore(flags);
+}
void MULTICALL_flush_page_update_queue(void)
{
unsigned long flags;
+ unsigned int _idx;
spin_lock_irqsave(&update_lock, flags);
- if ( idx != 0 )
+ if ( (_idx = idx) != 0 )
{
#if MMU_UPDATE_DEBUG > 1
printk("Flushing %d entries from pt update queue\n", idx);
#if MMU_UPDATE_DEBUG > 0
DEBUG_allow_pt_reads();
#endif
+ idx = 0;
+ wmb(); /* Make sure index is cleared first to avoid double updates. */
queue_multicall2(__HYPERVISOR_mmu_update,
(unsigned long)update_queue,
- idx);
- idx = 0;
+ _idx);
}
spin_unlock_irqrestore(&update_lock, flags);
}
static inline void __flush_page_update_queue(void)
{
+ unsigned int _idx = idx;
#if MMU_UPDATE_DEBUG > 1
printk("Flushing %d entries from pt update queue\n", idx);
#endif
#if MMU_UPDATE_DEBUG > 0
DEBUG_allow_pt_reads();
#endif
- HYPERVISOR_mmu_update(update_queue, idx);
idx = 0;
+ wmb(); /* Make sure index is cleared first to avoid double updates. */
+ HYPERVISOR_mmu_update(update_queue, _idx);
}
void _flush_page_update_queue(void)
"b" (req), "c" (count) : "memory" );
if ( unlikely(ret < 0) )
+ {
+ extern void show_trace(unsigned long *);
+ show_trace(NULL);
panic("Failed mmu update: %p, %d", req, count);
+ }
return ret;
}